#include <system/mod.h>
#include <system/config.h>
#include <klibc/string.h>
#include <klibc/ctype.h>
#include <klibc/malloc.h>
#include <shell/shell.h>


/*
 * Inizializza il gestore dei moduli
 */
void mod_init()
{
	mod_list = NULL;
	mod_id = 0;
	
	strcpy(mod_type_strings[0], "Network Device");
	strcpy(mod_type_strings[1], "Graphics Device");
	strcpy(mod_type_strings[2], "Audio Device");
	strcpy(mod_type_strings[3], "Human Interface Device");
	strcpy(mod_type_strings[4], "Storage Device");
	strcpy(mod_type_strings[13], "FS");
	strcpy(mod_type_strings[14], "System");
	strcpy(mod_type_strings[15], "Unknow");
}


/*
 * Aggiunge un modulo alla lista
 */
void mod_add(module_t *m)
{
	if(m != NULL)
	{
		if(m->init != NULL)
		{
			m->next = mod_list;
			m->loaded = 0;
			mod_list = m;
		}
	}
}


/*
 * Elimina un modulo dalla lista
 */
void mod_del(module_t *m)
{
	module_t *p = mod_get_prev(m);
	
	if(m->loaded)
		mod_shutm(m);
		
	p->next = m->next;
	free(m);
}


/*
 * Controlla se un modulo esiste
 */
uint8_t mod_check(char *n)
{
	module_t *t = mod_list;
	
	while(t != NULL)
	{
		if(strcmp(t->name, n) == 0) return 1;
		t = t->next;
	}
	return 0;
}


/*
 * Controlla se le dipendenze sono rispettate, i moduli necessari vengono 
 * caricati
 */
uint8_t mod_depok(module_t *m)
{
	char tmod[32];
	char *dep = m->dep;
	uint8_t ind = 0;
	
	while(*dep != '\0')
	{
		tmod[ind] = *dep;		
		dep++;
		ind++;
		
		if(*dep == ' ' || *dep == '\0' || *dep == '\n')
		{
			tmod[ind] = '\0';
			if(mod_check(tmod))
			{
				if(!mod_get(tmod)->loaded)
					mod_initm(mod_get(tmod), NULL);
			}
			else
				return MOD_STATE_DEP_ER;
		}
	}
	
	return MOD_STATE_OK;
}


/*
 * Salva la lista dei moduli avviati
 */
uint8_t mod_list_save(char *fn)
{
	int i = 0;
	//FILE *f = fopen(f, "w");
	int f;
	
	if(f != NULL)
	{
		module_t *t = mod_list;
		
		while(t != NULL)
		{
			if(t->loaded)
			{
				//fprintf("%s\n", t->name);
				i++;
			}
			t = t->next;
		}
		
		//fclose(f);		
	}
	
	return i;	
}


/*
 * Carica la lista dei moduli avviati
 */
uint8_t mod_list_load(char *fn)
{
	int i = 0;
	//FILE *f = fopen(f, "r");
	char nm[128];
	int f;
	
	if(f != NULL)
	{
		/*while(fscanf("%s", nm))
		{
			device_initd(nm);
			i++;
		}
		
		fclose(f);*/		
	}
	
	return i;	
}


/*
 * Restituisce il numero dei moduli
 */
uint32_t mod_get_num()
{
	module_t *t = mod_list;
	uint32_t n = 0;
	
	while(t != NULL)
	{
		n++;
		t = t->next;
	}
	return n;
}


/*
 * Configurazione da parte dell'utente
 */
uint32_t mod_uconfig()
{
	char ch[255];
	module_t *t = mod_list;
	
	video_color_puts(THEME_START_COLOR, THEME_START_MSG);
	printf("Modules Config (y/n): ");
	scanf("%s", ch);
	if(ch[0] != 'y') return;
			
	
	while(t != NULL)
	{	
		if(t->loaded == 0)
		{
			do
			{
				puts("\t");
				video_color_puts(THEME_START_COLOR, THEME_START_MSG);
				printf("Active %s (%s) (y/n): ", t->name, mod_type_strings[t->type]); 
				scanf("%s", ch);
			}
			while(ch[0] != 'y' && ch[0] != 'n');
			if(ch[0] == 'y')
			{
				puts("\t");
				video_gotoxy(video_get_dimx()-7, video_get_y()-1);
				
				if(mod_initm(mod_get(t->name), NULL) == MOD_STATE_OK)
					video_color_puts(THEME_START_COLOR, "loaded\n");
				else
					video_color_puts(THEME_STATE_ER, " error\n");
			}
		}
		
		// Per ora non ci serve
		if(1==0) //else
		{
			do
			{
				puts("\t");
				video_color_puts(THEME_START_COLOR, THEME_START_MSG);
				printf("Deactive %s (%s) (y/n): ", t->name, mod_type_strings[t->type]); 
				scanf("%s", ch);
			}
			while(ch[0] != 'y' && ch[0] != 'n');
			if(ch[0] == 'y')
			{
				puts("\t");
				video_gotoxy(video_get_dimx()-7, video_get_y()-1);
				
				if(mod_shutm(mod_get(t->name)) == MOD_STATE_OK)
					video_color_puts(THEME_START_COLOR, "loaded\n");
				else
					video_color_puts(THEME_STATE_ER, " error\n");
			}
		}		
		
		t = t->next;
	}	
}


/*
 * Inizializza un modulo, pero' prima guarda se le dipendenze son rispettate
 * se non lo sono, prova a vedere se ci sono i moduli richiesti, altrimenti
 * restituisce un errore
 */
uint32_t mod_initm(module_t *m, char *arg)
{
	if(m != NULL)
	{
		if(m->init != NULL)
		{
			if(mod_depok(m) == MOD_STATE_OK)
			{
				if(m->loaded)
					return MOD_STATE_ALRD_ER;
					
				m->init(arg);
				m->loaded = 1;
				return MOD_STATE_OK;
			}
			else return MOD_STATE_DEP_ER;
		}
	}
	
	return MOD_STATE_NULL_ER;
}


/*
 * Deinizializza un modulo
 */
uint32_t mod_shutm(module_t *m)
{
	if(m != NULL)
	{
		if(m->shut != NULL)
		{
			if(!m->loaded)
				return MOD_STATE_ALRD_ER;
					
			m->shut();
			m->loaded = 0;
			return MOD_STATE_OK;
		}
	}
	
	return MOD_STATE_NULL_ER;	
}


/*
 * Restituisce un modulo da una stringa
 */
module_t *mod_get(char *n)
{
	module_t *m = mod_list;
	
	while(m != NULL)
	{
		if(strcmp(m->name, n) == 0) return m;
		
		m = m->next;
	}
	
	return (module_t *) mod_get("null");//NULL;
}


/*
 * Restituisce il modulo precedente di un modulo
 */
module_t *mod_get_prev(module_t *t)
{
	module_t *m = mod_list;
	
	while(m->next != NULL)
	{
		if(strcmp(m->next->name, t->name) == 0) return m;
		
		m = m->next;
	}
	
	return (module_t *) NULL;	
}


/*
 * Command for builtin shell
 */
void mod_command()
{
	int i = 0;
	if(argc >=1)
	{
		if(strcmp(argv[1], "-ac") == 0 || strcmp(argv[1], "--auto-config" ) == 0) printf("not yet implemented\n");
		else if(strcmp(argv[1], "-uc") == 0 || strcmp(argv[1], "--user-config" ) == 0) mod_uconfig();
		else if(strcmp(argv[1], "-lim") == 0 || strcmp(argv[1], "--list-init-mod" ) == 0)
		{
			module_t *t = mod_list;
			
			puts("\t");
			video_color_puts(THEME_START_COLOR, THEME_START_MSG);
			printf("Loaded and initialized modules\n");
			
			while(t != NULL)
			{
				if(t->loaded)
				{
					puts("\t");
					video_color_puts(THEME_START_COLOR, THEME_START_MSG);
					printf("%s (%s)\n",t->name, mod_type_strings[t->type]);
				}
				t = t->next;
			}			
		}
		else if(strcmp(argv[1], "-lm") == 0 || strcmp(argv[1], "--list-mod" ) == 0)
		{
			module_t *t = mod_list;
			
			puts("\t");
			video_color_puts(THEME_START_COLOR, THEME_START_MSG);
			printf("Loaded modules\n");
						
			while(t != NULL)
			{
				puts("\t");
				video_color_puts(THEME_START_COLOR, THEME_START_MSG);
				printf("%s (%s) (loaded = %d)\n",t->name, mod_type_strings[t->type], t->loaded);
				t = t->next;
			}
		}
		else if(strcmp(argv[1], "-l") == 0 || strcmp(argv[1], "--load" ) == 0) printf("not yet implemented\n");
		else if(strcmp(argv[1], "-r") == 0 || strcmp(argv[1], "--release" ) == 0)
		{
			if(argc >= 2)
			{
				module_t *a = mod_get(argv[2]);
				if(a != NULL)
				{
					mod_del(a);
				}
			}
		}
		else if(strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--dep" ) == 0)
		{
			if(argc >= 2)
			{
				module_t *a = mod_get(argv[2]);
				if(a != NULL)
				{
					if(a->dep[0] != '\0')
					{
						puts(a->dep);
						puts("\n");
					}
				}
			}
		}
		else if(strcmp(argv[1], "-mi") == 0 || strcmp(argv[1], "--mod-info" ) == 0)
		{
			if(argc >= 2)
			{
				module_t *a = mod_get(argv[2]);
				if(a != NULL)
				{
					printf(	"module name: %s\nmodule type: %s\nmodule description: %s\nmodule author: %s\n", 
							a->name, mod_type_strings[a->type], a->desc, a->author);
					printf(	"module dep: %s\nmodule license: %s\nmodule id: %d\nis module initialized? %d\n",
							a->dep, a->license, a->id, a->loaded);
				}
			}
		}		
		else if(strcmp(argv[1], "-i") == 0 || strcmp(argv[1], "--init" ) == 0)
		{
			if(argc >= 2)
			{
				for (i = 2; i < argc; i++)
				{
					puts("\t");
					video_color_puts(THEME_START_COLOR, THEME_START_MSG);
					printf("Initializing module '%s'",argv[i]);
					
					video_gotoxy(video_get_dimx()-7, video_get_y());
					
					if(mod_initm(mod_get(argv[i]), NULL) == MOD_STATE_OK)
						video_color_puts(THEME_START_COLOR, "loaded\n");
					else
						video_color_puts(THEME_STATE_ER, " error\n");
				}
			}
		}
		else if(strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--shut" ) == 0)
		{
			if(argc >= 2)
			{
				for (i = 2; i < argc; i++)
				{
					puts("\t");
					video_color_puts(THEME_START_COLOR, THEME_START_MSG);
					printf("Stopping module '%s'",argv[i]);
					
					video_gotoxy(video_get_dimx()-8, video_get_y());
					
					if(mod_shutm(mod_get(argv[i])) == MOD_STATE_OK)
						video_color_puts(THEME_STATE_WA, "stopped\n");
					else
						video_color_puts(THEME_STATE_ER, "  error\n");
				}
			}
		}
		else if(strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help" ) == 0)
		{
			printf("%s %%opts [%%modules]\n", argv[0]);
			printf("\t-ac, --auto-config      -       auto detect nedded modules\n");
			printf("\t-uc, --user-config      -       ask to user what modules start\n");
			printf("\t-lim, --list-init-mod   -       list initialized modules\n");
			printf("\t\t-t, --type %d         -       of this type\n");
			printf("\t-lm, --list-mod         -       list all modules\n");
			printf("\t\t-t, --type %d         -       of this type\n");
			printf("\t-l, --load              -       load a module from file\n");
			printf("\t-r, --remove            -       remove a module\n");
			printf("\t-i, --init              -       start a module\n");
			printf("\t-s, --shut              -       stop a module\n");
			printf("\t-d, --dep               -       show module dependencies\n");
			printf("\t-mi, --mod-info         -       show module info\n");
			printf("\t-h, --help              -       this help\n");
			 
		}
		else printf("bad option %s\n", argv[1]);
	}
}


/*
 * Funzioni da mettere quando il modulo non ne offre
 */
uint32_t mod_rwop(struct file *f, uint8_t *data, uint32_t size, uint64_t offset)
{
	return 0;
}

uint32_t mod_ocop(struct file *f)
{
	return 0;
}

